﻿using System;
using System.Threading;
using Omniscient.Foundation.ApplicationModel;
using Omniscient.Foundation.Commanding;
using Omniscient.Foundation.Data;
using Omniscient.TimeTracker.BusinessModel.Commands;
using Omniscient.TimeTracker.Data;

namespace Omniscient.TimeTracker.BusinessModel
{
    public class TimeEntryManager : ITimeEntryManager
    {
        private DelegateCommandHandler _changeTaskCategoryCommandHandler;
        private DelegateCommandHandler _startTaskCommandHandler;

        public event NewTaskStartedEventHandler NewTaskStarted;
        public event StateChangedEventHandler StateChanged;

        public TimeEntryManager()
        { 
            CurrentRunningTimeEntry = null;
            TimeEntries = new EntityList<TimeEntry>();

            _changeTaskCategoryCommandHandler = new DelegateCommandHandler(this.ChangeTaskCategoryCommandHandler);
            _startTaskCommandHandler = new DelegateCommandHandler(this.StartTaskCommandHandler);

            TransitiveCommand cmd;
            cmd = ApplicationManager.Current.ObjectContainer.Get<StartTaskCommand>();
            if (cmd != null) cmd.RegisterHandler(_startTaskCommandHandler);
        }

        private void ChangeTaskCategoryCommandHandler(object param)
        {
            if (this.CurrentRunningTimeEntry == null) throw new InvalidOperationException("There's no current running time entry.");
            if (param == null) throw new ArgumentNullException("param");

            this.StartTask(this.CurrentRunningTimeEntry.Task, (Category)param);
        }

        private void StartTaskCommandHandler(object param)
        {
            if (param == null) throw new ArgumentNullException("param");

            this.StartTask((Task)param, new Category(new Guid("{8B2DA174-0092-4818-B118-BD01276F84F6}"), false));
        }

        private void StopTaskCommandHandler(object param)
        {
            if (CurrentRunningTimeEntry == null) throw new InvalidOperationException("There is no time entry currently running.");

            string comments;
            if (param != null && param is string) comments = param.ToString();
            else comments = string.Empty;

            this.StopCurrentTimeEntry(comments, false);
        }

        public TimeEntry StartTask(Task task)
        { 
            return StartTask(task, null, null);
        }

        public TimeEntry StartTask(Task task, Category category)
        {
            return StartTask(task, category, null);
        }

        public TimeEntry StartTask(Task task, string LastTimeEntryComments)
        {
            return StartTask(task, null, LastTimeEntryComments);
        }

        /// <summary>
        /// Start a new time entry. If an time entry is already started, it swap the task.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="category"></param>
        /// <returns></returns>
        public TimeEntry StartTask(Task task, Category category, string LastTimeEntryComments)
        {
            //If another task is already started, stop it
            //then start the new task (swap running task)
            if (HasRunningTask)
            { 
                StopCurrentTimeEntry(LastTimeEntryComments, true);
            }
                

            //Setup new time entry
            TimeEntry newTimeEntry;
            newTimeEntry = new TimeEntry();
            newTimeEntry.Task = task;
            newTimeEntry.StartDateTime = DateTime.Now;
            //todo: attach correct resource.
            IResourceService resourceService;
            resourceService = ApplicationManager.Current.ServiceProvider.GetService<IResourceService>();

            EntityList<Resource> resources = resourceService.GetResources();
            //todo: add to ObjectContainer


            string username = Thread.CurrentPrincipal.Identity.Name;

            foreach (Resource r in resources)
            {
                if (r.UserName == username )
                {
                    newTimeEntry.Resource = r;
                }
            }

            CurrentRunningTimeEntry = newTimeEntry;
            
            //TimeEntries.Add(newTimeEntry);

            if (category != null)
                newTimeEntry.Category = category;

            if (!HasRunningTask)
            {
                HasRunningTask = true;
                if (StateChanged != null) StateChanged(this, EventArgs.Empty);
                
                //register a command listener to change category of current time entry
                TransitiveCommand cmd;
                cmd = ApplicationManager.Current.ObjectContainer.Get<ChangeTaskCategoryCommand>();
                if (cmd != null) cmd.RegisterHandler(_changeTaskCategoryCommandHandler);
            }

            if (NewTaskStarted != null) NewTaskStarted(this, EventArgs.Empty);

            return CurrentRunningTimeEntry;
        }

        public TimeEntry StopTask()
        {
            return StopTask(string.Empty);
        }

        public TimeEntry StopTask(string comments)
        {
            //Remove the listener for the Change Category command (since there's no current entry on which to change category...)
            TransitiveCommand cmd;
            cmd = ApplicationManager.Current.ObjectContainer.Get<ChangeTaskCategoryCommand>();
            if (cmd != null) cmd.UnregisterHandler(_changeTaskCategoryCommandHandler);
            return StopCurrentTimeEntry(comments, false);
        }

        private TimeEntry StopCurrentTimeEntry(string comments, bool keepsRunning)
        {
            TimeEntry current;

            current = CurrentRunningTimeEntry;
            if (!keepsRunning)
            {
                HasRunningTask = false;
                if (StateChanged != null) StateChanged(this, EventArgs.Empty);
            }
            if (current != null)
            {
                //Save the current running time entry
                current.EndDateTime = DateTime.Now;
                current.Comments = comments;
                SaveCurrentTimeEntry();

                //Cleanup Current running task and time entry 
                CurrentRunningTimeEntry = null;
            }

            return current;
        }

        private void SaveCurrentTimeEntry()
        {
            IEntityAdapter<TimeEntry> adapter;
            adapter = ApplicationManager.Current.ServiceProvider.GetService<IEntityAdapter<TimeEntry>>();
            if (adapter == null) return;

            adapter.Save(CurrentRunningTimeEntry);

            IEntityController<TimeEntry> controller;
            controller = ApplicationManager.Current.ObjectContainer.Get<IEntityController<TimeEntry>>();
            if (controller == null) return;
            controller.AcceptChanges(CurrentRunningTimeEntry);
        }

        public EntityList<TimeEntry> TimeEntries { get; internal set; }
        public TimeEntry CurrentRunningTimeEntry { get; internal set; }
                
        public bool HasRunningTask { get; private set; }

    }

}
